using System;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using JlPay.Sdk.Utils;

namespace JlPay.Sdk.Core
{
    /// <summary>
    /// 默认签名验证器
    /// </summary>
    /// <param name="config">配置对象</param>
    public class DefaultSignVerifier(Config config) : ISignVerifier
    {
        private readonly Config _config = config ?? throw new ArgumentNullException(nameof(config));

        /// <summary>
        /// 使用SM3WithSM2签名
        /// </summary>
        /// <param name="method">HTTP方法</param>
        /// <param name="uri">请求URI</param>
        /// <param name="body">请求体</param>
        /// <returns>签名、时间戳、随机数</returns>
        public (string sign, string timestamp, string nonce) Sign(string method, string uri, object? body)
        {
            // 获取当前时间戳（精确到秒）
            var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();

            // 生成随机字符串
            var nonceBytes = new byte[16];
            using (var rng = RandomNumberGenerator.Create())
            {
                rng.GetBytes(nonceBytes);
            }
            var nonce = SmUtils.ToHexString(nonceBytes);

            // 将请求体转为JSON字符串
            var bodyStr = string.Empty;
            if (body != null)
            {
                // 检查是否是字符串类型（原始JSON字符串）
                if (body is string strBody)
                {
                    bodyStr = strBody;
                }
                else
                {
                    // 对其他类型进行JSON序列化
                    bodyStr = JsonSerializer.Serialize(body, new JsonSerializerOptions 
                    { 
                        PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
                        WriteIndented = false,
                        Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping // 不转义Unicode字符
                    });
                }
            }

            // 构造签名字符串
            var signStr = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{bodyStr}\n";
            string signature;
            try
            {
                signature = SmUtils.SM2Sign(Encoding.UTF8.GetBytes(signStr), _config.PrivateKey);
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException($"SM2签名失败: {ex.Message}", ex);
            }

            return (signature, timestamp, nonce);
        }

        /// <summary>
        /// 验证SM3WithSM2签名
        /// </summary>
        /// <param name="method">HTTP方法</param>
        /// <param name="uri">请求URI</param>
        /// <param name="timestamp">时间戳</param>
        /// <param name="nonce">随机数</param>
        /// <param name="body">响应体</param>
        /// <param name="signature">签名</param>
        public void Verify(string method, string uri, string timestamp, string nonce, object? body, string signature)
        {
            // 将响应体转为JSON字符串
            var bodyStr = string.Empty;
            if (body != null)
            {
                if (body is string strBody)
                {
                    bodyStr = strBody;
                }
                else
                {
                    // 使用不转义Unicode字符的JSON序列化选项
                    bodyStr = JsonSerializer.Serialize(body, new JsonSerializerOptions 
                    { 
                        PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
                        WriteIndented = false,
                        Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping // 不转义Unicode字符
                    });
                }
            }

            // 构造签名字符串
            var signStr = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{bodyStr}\n";

            // 验证签名
            bool isValid;
            try
            {
                isValid = SmUtils.SM2Verify(Encoding.UTF8.GetBytes(signStr), signature, _config.JlpayPublicKey);
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException($"验证签名失败: {ex.Message}", ex);
            }

            if (!isValid)
            {
                throw new InvalidOperationException("签名验证不通过");
            }
        }
    }
} 